﻿
module NHibernateTests

open NHibernate
open NHibernate.Cfg
open NHibernate.Driver
open NHibernate.Dialect
open NHibernate.ByteCode.Castle
open NHibernate.Connection
open NHibernate.Type
open NHibernate.UserTypes
open Castle.ActiveRecord
open Castle.ActiveRecord.Framework.Config
open ActiveRecordExtensions

/// From http://geekswithblogs.net/opiesblog/archive/2006/08/13/87880.aspx
/// http://code.google.com/p/ncommon/source/browse/trunk/NCommon.NHibernate/src/CompositeUserTypeBase.cs
type UnionCompositeUserType<'a when 'a: equality>() =
    interface ICompositeUserType with
        member x.IsMutable with get() = true
        member x.PropertyTypes with get() = [| NHibernateUtil.Class; NHibernateUtil.String |]
        member x.PropertyNames with get() = [| "type"; "value" |]
        member x.GetPropertyValue (comp, property) = 
            match property with
            | 0 -> upcast comp.GetType()
            | 1 -> comp.GetType().GetProperty("Item").GetValue(comp, null)
            | x -> failwith (sprintf "Unexpected index %A" x)
        member x.SetPropertyValue (comp, property, value) =
            printfn "%A %A" property value
        member x.ReturnedClass with get() = typeof<'a>
        member x.Equals (a,b) = a = b
        member x.NullSafeGet (dr, names, session, owner) = 
            upcast x
        member x.NullSafeSet (cmd, value, index, session) = ()
        member x.DeepCopy v = v
        member x.Disassemble (value, session) = (x :> ICompositeUserType).DeepCopy value
        member x.Assemble (cached, session, owner) = (x :> ICompositeUserType).DeepCopy cached
        member x.Replace (original, target, session, owner) = original
        member x.GetHashCode v = v.GetHashCode()

type RequestInfo =  
    | Id of int
    | Name of string    

[<ActiveRecord>]
type Person() =
    [<DefaultValue>]
    val mutable _id: int
    [<PrimaryKey>]
    member x.Id with get() = x._id and set(v) = x._id <- v
    
    [<DefaultValue>]
    val mutable _request: RequestInfo
    [<CompositeUserType>]
    member x.Request with get() = x._request and set(v) = x._request <- v
    

let nhtests () =
    let sessionFactoryProperties = dict [
                                    Environment.ConnectionDriver, typeof<SQLite20Driver>.FullName
                                    Environment.ProxyFactoryFactoryClass, typeof<ProxyFactoryFactory>.AssemblyQualifiedName
                                    Environment.Dialect, typeof<SQLiteDialect>.FullName
                                    Environment.ConnectionProvider, typeof<DriverConnectionProvider>.FullName
                                    Environment.ConnectionString, "Data Source=test.db;Version=3;New=True;"
    ]
    let arConfig = InPlaceConfigurationSource()
    arConfig.GetType().GetMethod("SetDebugFlag", System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance).Invoke(arConfig, [| true |]) |> ignore
    arConfig.Add(typeof<ActiveRecordBase>, sessionFactoryProperties)
    ActiveRecordStarter.ResetInitializationFlag()
    ActiveRecordStarter.FModelCreated
        |> Event.add (fun (model, config) -> 
            model.CompositeUserType
            |> Seq.map (fun p -> (p, p.MemberType.GetCustomAttributes(typeof<CompilationMappingAttribute>, true)))
            |> Seq.filter (fun (p,a) -> a.Length > 0)
            |> Seq.map (fun (p,a) -> (p, a.[0] :?> CompilationMappingAttribute))
            |> Seq.filter (fun (p,a) -> a.SourceConstructFlags = SourceConstructFlags.SumType)
            |> Seq.iter (fun (p,a) -> 
                            let gtype = typeof<UnionCompositeUserType<_>>.GetGenericTypeDefinition().MakeGenericType [| p.MemberType |]
                            p.Attribute.CompositeType <- gtype
                            let userType = System.Activator.CreateInstance gtype :?> ICompositeUserType
                            p.Attribute.ColumnNames <- userType.PropertyNames))
    ActiveRecordStarter.Initialize(arConfig, typeof<Person>)
    ActiveRecordStarter.CreateSchema()
    ActiveRecordMediator.SaveAndFlush (Person(Request = RequestInfo.Name "someone"))
    
    ()